Skip to content

Make particle gun parameters configurable and add tracking scan#1158

Open
olantwin wants to merge 4 commits into
masterfrom
tracking-benchmark-scan
Open

Make particle gun parameters configurable and add tracking scan#1158
olantwin wants to merge 4 commits into
masterfrom
tracking-benchmark-scan

Conversation

@olantwin

@olantwin olantwin commented Apr 24, 2026

Copy link
Copy Markdown
Contributor
  • Make particle gun polar angle and multiplicity configurable via --thetaMin, --thetaMax, --nTracks
  • Add run_tracking_scan.py to sweep tracking benchmark over angle and multiplicity grids
  • Add charge-ID efficiency metric and --mixCharges flag for particle/antiparticle generation

Checklist

Summary by CodeRabbit

  • New Features

    • Particle gun now supports configurable polar-angle bounds and variable track multiplicity per event
    • New scanning tool enables automated benchmark sweeps across angle and multiplicity grids
    • Charge identification efficiency metrics added with optional charge-mixing for mixed particle/antiparticle events
  • Bug Fixes

    • Enhanced handling of undefined values in efficiency calculations, displaying appropriate "undefined" indicators when data is unavailable

@olantwin olantwin requested a review from a team as a code owner April 24, 2026 15:09
@coderabbitai

coderabbitai Bot commented Apr 24, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@olantwin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 15 minutes and 41 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6898b53c-8478-4e2a-903f-016089cd4639

📥 Commits

Reviewing files that changed from the base of the PR and between 16fb7fa and 22897ca.

📒 Files selected for processing (3)
  • macro/run_simScript.py
  • macro/run_tracking_scan.py
  • python/tracking_benchmark.py
📝 Walkthrough

Walkthrough

The changes extend a tracking benchmark suite to support configurable polar-angle ranges and multiple-track generation. A new scan script orchestrates benchmarks across angle and multiplicity parameter grids, while the metrics module now handles charge identification and properly represents undefined metric values when denominators are zero.

Changes

Cohort / File(s) Summary
Documentation
CHANGELOG.md
Added three new features to the Unreleased section: configurable theta bounds, multi-track generation via --nTracks, new scan script, and charge-ID efficiency metrics with --mixCharges flag.
Particle Gun Configuration
macro/run_simScript.py
Extended Particle Gun mode with polar-angle bounds (thetaMin/thetaMax) instead of fixed angle, and --nTracks for multiple particles per event. When --mixCharges and nTracks > 1, splits tracks across two generators with opposite-sign PDG IDs.
Benchmark Simulation
macro/run_tracking_benchmark.py
Added CLI arguments for thetaMin, thetaMax, --nTracks, and --mixCharges. Improved path handling with absolute path conversion and os.path.join. Generalized run_phase() to accept subprocess.run keyword arguments and runs reconstruction phase with cwd=outputDir.
Benchmark Scanning Orchestration
macro/run_tracking_scan.py
New 383-line script to orchestrate full tracking scans over polar angle and multiplicity grids. Computes scan points per mode (theta, nTracks, or grid), runs run_tracking_benchmark.py for each point, aggregates results into scan_results.json, generates ROOT TGraphErrors summaries, and produces optional matplotlib plots.
Metrics Calculation & Charge-ID
python/tracking_benchmark.py
Updated wilson_interval() to return None for zero denominators. Modified efficiency metrics (efficiency, clone_rate, ghost_rate) to propagate None when denominators are zero. Added charge-ID matching during MC/reco reconstruction and new charge-ID efficiency metrics with counters. Updated print_summary() formatter to display undefined (no data) for None values.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as run_tracking_scan.py
    participant Benchmark as run_tracking_benchmark.py
    participant Sim as run_simScript.py
    participant Metrics as tracking_benchmark.py
    participant Results as Result Files

    User->>CLI: run with scan parameters
    loop Per scan point (theta, nTracks)
        CLI->>CLI: Compute scan point parameters
        CLI->>Benchmark: Launch with theta bounds & nTracks
        Benchmark->>Sim: Invoke simulation subprocess
        Sim->>Sim: Generate particles with (theta, nTracks, mixCharges)
        Sim->>Metrics: Create tracking_metrics.json
        Benchmark->>Metrics: Parse output/metrics
        Benchmark->>Results: Write point JSON
        CLI->>CLI: Load tracking_metrics.json
        CLI->>CLI: Extract & append to scan results
    end
    CLI->>Results: Write scan_results.json
    CLI->>Results: Generate ROOT TGraphErrors (scan_summary.root)
    CLI->>Results: Generate matplotlib plots (optional)
    CLI->>User: Report scan completion & output paths
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: making particle gun parameters configurable and adding a tracking scan utility.
Description check ✅ Passed The description covers the key changes, includes a completed checklist matching the template, and specifies the three main feature additions.
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tracking-benchmark-scan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@olantwin olantwin requested a review from kholoimov April 24, 2026 16:10
Add --thetaMin, --thetaMax, and --nTracks arguments to the PG
subcommand in run_simScript.py (previously hardcoded to theta=0,
nTracks=1). Wire these through run_tracking_benchmark.py so the
tracking benchmark can be run at arbitrary angles and multiplicities.
New script run_tracking_scan.py that sweeps the tracking benchmark
over a grid of polar angles and track multiplicities. Includes
charge-ID efficiency, --mixCharges flag, null handling for undefined
metrics, points-only plots with density annotation.
@olantwin olantwin force-pushed the tracking-benchmark-scan branch from 103f7bb to 16fb7fa Compare April 28, 2026 09:18
@olantwin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 28, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
macro/run_tracking_scan.py (2)

155-156: Minor duplication: gun_area_m2 computed in two places.

The gun area calculation 200 * 300 * 1e-4 appears at lines 156 and 284. Consider extracting it to a constant near the top of the file.

♻️ Extract constant
+# Gun area from benchmark defaults: Dx=200cm, Dy=300cm → m²
+GUN_AREA_M2 = 200 * 300 * 1e-4

 # ... in the scan loop:
-    gun_area_m2 = 200 * 300 * 1e-4
     all_results.append(
         {
             ...
-            "density_per_m2": round(n_tracks / gun_area_m2, 4),
+            "density_per_m2": round(n_tracks / GUN_AREA_M2, 4),

 # ... in matplotlib section:
-    gun_area_m2 = 200 * 300 * 1e-4  # Dx=200, Dy=300 cm
+    # GUN_AREA_M2 already defined at module level

Also applies to: 284-284

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@macro/run_tracking_scan.py` around lines 155 - 156, The calculation for
gun_area_m2 is duplicated; extract the value into a single constant (e.g.,
GUN_AREA_M2) defined near the top of the module and replace both occurrences of
the literal expression (200 * 300 * 1e-4) with that constant, updating uses of
gun_area_m2 in the functions/blocks that reference it so they read from
GUN_AREA_M2 and removing the duplicate local computation.

152-166: Consider adding error handling for JSON loading.

If the benchmark subprocess fails after creating but before completing the JSON file, json.load will raise an exception. While unlikely, adding a try-except would make the scan more robust.

♻️ Optional defensive check
-    with open(json_path) as f:
-        metrics = json.load(f)
+    try:
+        with open(json_path) as f:
+            metrics = json.load(f)
+    except (json.JSONDecodeError, KeyError) as e:
+        print(f"WARNING: Could not parse metrics from {json_path}: {e}")
+        continue
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@macro/run_tracking_scan.py` around lines 152 - 166, The json.load call
reading json_path can raise exceptions if the subprocess wrote an incomplete or
missing file; wrap the load in a try/except that catches FileNotFoundError,
json.JSONDecodeError (or json.decoder.JSONDecodeError) and any other Exception,
log a clear warning including json_path and the exception, and either skip
appending this result (or set metrics = {"tracking_benchmark": {}} / a safe
default) so that the subsequent all_results.append using
metrics["tracking_benchmark"] cannot crash; update the block around json.load,
json_path, metrics and the all_results.append to use the safe metrics value or
early-continue on error.
macro/run_simScript.py (1)

524-527: Consider validating particle type when --mixCharges is enabled.

The charge mixing logic assumes the particle has a valid antiparticle with PDG code -abs(pID). This works for charged particles like muons (±13) but would produce invalid PDG codes for neutral particles (e.g., photon 22 → -22 is undefined).

Consider adding a validation check or at least documenting that --mixCharges is only valid for charged particles.

♻️ Optional validation
 if options.command == "PG":
+    if options.mixCharges and options.nTracks > 1:
+        # Verify particle has a valid antiparticle (charged particle)
+        PDG = ROOT.TDatabasePDG.Instance()
+        particle = PDG.GetParticle(abs(options.pID))
+        if particle is None or particle.Charge() == 0:
+            print(f"WARNING: --mixCharges requested but pID={options.pID} is not a charged particle")
     if options.mixCharges and options.nTracks > 1:
         pids = [(abs(options.pID), options.nTracks // 2), (-abs(options.pID), (options.nTracks + 1) // 2)]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@macro/run_simScript.py` around lines 524 - 527, When options.mixCharges is
true, validate that the chosen particle is actually charged before constructing
pids: check options.pID and ensure abs(options.pID) is not a known neutral PDG
(e.g., 22, 111, 130, 2112) or, if a particle lookup API exists in the codebase,
use it to confirm the particle has an antiparticle; if the check fails,
raise/exit with a clear error or disable mixCharges. Update the branch that sets
pids (references: options.mixCharges, options.pID, pids, options.nTracks) to
perform this validation first and only produce the two-charge split when the
particle is validated as charged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@python/tracking_benchmark.py`:
- Around line 300-314: The current broad "except Exception: pass" around the
charge-identification block (involving fitted_state.getCharge(),
self.sim_tree.MCTrack[mc_id].GetPdgCode(), self.PDG.GetParticle and mc_particle)
silently swallows errors—replace it so exceptions are either narrowed to the
specific GenFit/expected exception type or captured as "except Exception as e"
and logged (e.g., using the module logger or logging.exception) with context
like mc_id and p_truth to aid debugging, rather than silently passing.

---

Nitpick comments:
In `@macro/run_simScript.py`:
- Around line 524-527: When options.mixCharges is true, validate that the chosen
particle is actually charged before constructing pids: check options.pID and
ensure abs(options.pID) is not a known neutral PDG (e.g., 22, 111, 130, 2112)
or, if a particle lookup API exists in the codebase, use it to confirm the
particle has an antiparticle; if the check fails, raise/exit with a clear error
or disable mixCharges. Update the branch that sets pids (references:
options.mixCharges, options.pID, pids, options.nTracks) to perform this
validation first and only produce the two-charge split when the particle is
validated as charged.

In `@macro/run_tracking_scan.py`:
- Around line 155-156: The calculation for gun_area_m2 is duplicated; extract
the value into a single constant (e.g., GUN_AREA_M2) defined near the top of the
module and replace both occurrences of the literal expression (200 * 300 * 1e-4)
with that constant, updating uses of gun_area_m2 in the functions/blocks that
reference it so they read from GUN_AREA_M2 and removing the duplicate local
computation.
- Around line 152-166: The json.load call reading json_path can raise exceptions
if the subprocess wrote an incomplete or missing file; wrap the load in a
try/except that catches FileNotFoundError, json.JSONDecodeError (or
json.decoder.JSONDecodeError) and any other Exception, log a clear warning
including json_path and the exception, and either skip appending this result (or
set metrics = {"tracking_benchmark": {}} / a safe default) so that the
subsequent all_results.append using metrics["tracking_benchmark"] cannot crash;
update the block around json.load, json_path, metrics and the all_results.append
to use the safe metrics value or early-continue on error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55c97e0c-de7c-4758-bba0-d0398a546483

📥 Commits

Reviewing files that changed from the base of the PR and between 22346f8 and 16fb7fa.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • macro/run_simScript.py
  • macro/run_tracking_benchmark.py
  • macro/run_tracking_scan.py
  • python/tracking_benchmark.py

Comment thread python/tracking_benchmark.py Outdated
- Log fitted-state exceptions instead of silently swallowing them
- Validate particle is charged before applying mixCharges
- Extract duplicated gun_area_m2 to module-level GUN_AREA_M2 constant
- Wrap json.load in error handling for corrupt/missing result files
@olantwin olantwin removed request for a team and antonioiuliano2 April 28, 2026 12:00
@olantwin

Copy link
Copy Markdown
Contributor Author

Will be partially superseded by #1183

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant